feat(tui): add a global/project context switcher for per-project configs and installs#502
Draft
CorticalCode wants to merge 3 commits into
Draft
feat(tui): add a global/project context switcher for per-project configs and installs#502CorticalCode wants to merge 3 commits into
CorticalCode wants to merge 3 commits into
Conversation
…targetPath All load/save/backup, install-state readers, install/uninstall, and widget hook sync accept an explicit target file, defaulting to today's user-global settings.json. installStatusLine resolves its target once so a statusLine and its hooks always land in the same file. Re-derives the targetPath hook-routing approach from sirmalloc's 25595a4 onto current main. Groundwork for per-project installs (sirmalloc#351).
…ets by it Implements the context-switcher model sirmalloc proposed in sirmalloc#351: a scope singleton mirrors the initConfigPath idiom; project scope points the config at <root>/.claude/ccstatusline.json and installs into <root>/.claude/settings.local.json; an explicit --config (custom scope) and the piped/--hook paths behave exactly as before. The TUI branch of main() auto-detects an existing project config. Load-error messages now name the actual config file, hook sync no longer materializes an empty settings file on plain saves, and the refresh-interval writer resolves its target once so an in-flight scope switch cannot write across scopes.
ctrl+p flips between global and project mode from the main menu, with a header indicator showing the resolved project config path. Entering a project with no config offers to copy the global config or start with defaults, seeding in memory only; the file is created on explicit save. Switching with unsaved edits prompts save/discard/cancel, routed through the invalid-config save guard. Install dialogs show the scope-aware target and save the project config before installing so the installed --config never dangles. The project-config detection, copy-global seeding, and active-config indicator build on jackall3n's sirmalloc#58. Implements the switcher sirmalloc designed in sirmalloc#351; closes the settings.local.json request from sirmalloc#276.
Contributor
Author
|
Screenshots of the switcher states, captured from the TUI: Global mode. The only change existing users see is one header line: First switch into a project with no config (ctrl+p): Project mode after seeding. The header shows the resolved config path; Save & Exit appears because the seed is in memory only, and nothing exists on disk until you save: Switching away with unsaved edits (ctrl+p again): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A global/project context switcher, as designed in #351. Global mode is today's behavior, unchanged.
ctrl+pin the TUI switches to Project mode: the config lives in.claude/ccstatusline.json(committable, travels with the repo), and the install writes the statusLine and managed hooks to.claude/settings.local.json(personal) with--configon the command.This closes the loop on #276, the original request: an install in
settings.local.jsonsurvives company tooling that rewritessettings.json.How it behaves
ctrl+prespects the pinned-version-mismatch lock the same wayctrl+sdoes.--confignever references a missing file. Uninstall touches only the active mode's file and leaves.claude/ccstatusline.jsonin place.--configon the command line behaves exactly as today and disables switching. Piped rendering and--hookare untouched: they never auto-detect, verified against live renders.Design decisions (flagged for review)
--configuses an absolute path. The statusline command's working directory is not documented (Custom statusLine command resets shell CWD between Bash tool calls anthropics/claude-code#31471 suggests a fresh shell context), so a relative path is not guaranteed to resolve. Nothing portable is lost:settings.local.jsonis machine-local by definition..claude/ccstatusline.jsonbut installs ccstatusline themselves. This follows the design upthread; the alternative (statusLine in committed.claude/settings.json) was rejected for surprise-install risk.--hookhandler never loads any config; its cache writes are keyed only by home dir and session id, so the duplicate fire is one redundant spawn per event, never a conflict.25595a4, which predates two scopes being simultaneously valid.--configdisables the switcher. You asked for a specific file; the TUI does not second-guess it.ctrl+p, main menu only, no new menu rows.Caveats and known limitations
settings.json) is what this fixes.statusLinehonors normal settings precedence as of Claude Code v2.1.207. Anthropic has been narrowing project-scope settings (auto-mode settings leftsettings.local.jsonin v2.1.198; plugin configs left project settings in v2.1.207). If that trend ever reachesstatusLine, project-mode installs stop taking effect.installationmetadata lands in the committable.claude/ccstatusline.json(a consequence of the config-path lockstep with the existing metadata system). The copy-from-global seed strips it, but a project-mode install records it. Machine-specific churn in a shareable file; open to suggestions if you'd rather it be suppressed in project scope.saveClaudeSettingsremains a plain (non-atomic) write, pre-existing behavior now also applied tosettings.local.json. Symlinked targets are written through, not replaced. Note for any future atomic-write change there: it must carry symlink resolution or it will clobber user symlinks. (The ccstatusline config files themselves already get atomic, symlink-aware writes.) Happy to follow up with a shared atomic-write helper (temp file + rename with symlink resolution, reusing the pattern the ccstatusline config writes already use) as a separate PR..claude/ccstatusline.jsoncontents but not byte-formatting — the pre-existing metadata writer re-serializes it with schema defaults. Functionally intact, not byte-identical.settings.local.jsonwhile the real piped render reads the global file. Cosmetic.Verification
1680 tests (60+ new: target routing, scope resolution, key preservation for
sandbox/permissionsinsettings.local.json, hook colocation, a mid-flight scope-change race pin, switcher planning), lint clean, each of the three commits green independently. Zero-behavior-change for piped/--hook/--configpaths verified against live renders. A 12-item interactive walkthrough covered the full switcher flow, install/uninstall round-trips, symlinked config files and directories, and relaunch auto-detection.Feedback wanted specifically
--config(decision 2) are the two places this deviates from or interprets the How should per-project install work? — follow-up to #309 #351 sketch — confirm or redirect.installationmetadata in the committable config (caveat 3).ctrl+pbinding and the resolved-path header format, since they're pure UX taste.Credits
Builds on @jackall3n's #58 (project-config detection, copy-global seeding, active-path indicator) and the hook routing from
25595a4, re-derived on current main, per the split discussed in #351.